package com.bp.scoringgun.service.impl;

import com.bp.scoringgun.entity.*;


import com.bp.scoringgun.service.ContriService;
import com.bp.scoringgun.service.DataParserService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

@Service
public class DataParserServiceImpl implements DataParserService {

    @Autowired
    private DatabaseHandlerServiceImpl databaseHandlerService = new DatabaseHandlerServiceImpl();
    @Autowired
    private GitHubAPIServiceImpl gitHubAPIService ;
    private final ObjectMapper objectMapper = new ObjectMapper();

    private Map<Long, User> userMap = new HashMap<>();
    private Map<String, Contri> contriMap = new HashMap<>(); // 使用 userId + repoId 的组合键
    private Map<Long, Repo> repoMap = new HashMap<>();
    private Map<Long, Org> orgMap = new HashMap<>();
    private Map<String, UserOrg> userOrgMap = new HashMap<>();


    // 解析 JSON 文件的方法
    public String parseJsonFile(String filePath) {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                JsonNode jsonNode = objectMapper.readTree(line);
                processEvent(jsonNode);
            }
            System.out.println("Parser finished,writing to database...");
            writeToDatabase();
            return "success";
        } catch (IOException e) {
            return "Error reading or parsing the JSON file." ;
        }
    }
    //调用api查询地点并更新数据库
    public String writeLocationAndLanguage(){
        try {
            for (User user : userMap.values()) {
                String url = user.getUrl();
                String nation = gitHubAPIService.getLocation(url);
                user.setNation(nation);
                String languages = gitHubAPIService.getRepoLanguages(gitHubAPIService.getUserReposUrl(url));
                user.setField(languages);
                databaseHandlerService.writeUser(user);
            }
            for (Org org : orgMap.values()) {
                String url = org.getUrl();
                String location = gitHubAPIService.getLocation(url);
                org.setLocation(location);
                databaseHandlerService.writeOrg(org);
            }
            return "success";
        } catch (Exception e) {
            return "Error reading or parsing the JSON file.";
        }
    }
    //记录User
    private void addUserToMap(Long actorId, String url,String name) {
        if (userMap.containsKey(actorId)) {
            // 如果 actorId 已存在，则更新用户对象的 URL（根据需要）
            User existingUser = userMap.get(actorId);
            existingUser.setUrl(url); // 假设 User 类中有 setUrl 方法
        } else {
            // 如果 actorId 不存在，则创建新用户并插入到字典中
            User newUser = new User(); // 假设 User 类有构造方法 User(Long, String)
            newUser.setId(actorId);
            newUser.setUrl(url);
            newUser.setName(name);
            userMap.put(actorId, newUser);
        }
    }
    //清空Map
    public void clearMaps() {
        userMap.clear();
        contriMap.clear();
        repoMap.clear();
        orgMap.clear();
        userOrgMap.clear();
    }
    //记录Org
    private void addOrgToMap(Long OrgId, String url) {
        if (orgMap.containsKey(OrgId)) {
            Org existingOrg = orgMap.get(OrgId);
            existingOrg.setUrl(url);
        } else {
            Org newOrg = new Org();
            newOrg.setId(OrgId);
            newOrg.setUrl(url);
            orgMap.put(OrgId, newOrg);
        }
    }
    //记录UserOrg
    private void addUserOrgToMap(Long ActorId, Long OrgId) {
        String key = ActorId + "_" + OrgId;
        if (!userOrgMap.containsKey(key)) {
            UserOrg newUserOrg = new UserOrg();
            newUserOrg.setUserId(ActorId);
            newUserOrg.setOrgId(OrgId);
            userOrgMap.put(key, newUserOrg);
        }
    }

    private void setOrgAndActor(JsonNode eventNode){
        JsonNode actorNode = eventNode.get("actor");
        Long actorId = actorNode.get("id").asLong();
        String actorUrl = actorNode.get("url").asText();
        addUserToMap(actorId,actorUrl,actorNode.get("login").asText());
        if (eventNode.has("org")) {
            JsonNode orgNode = eventNode.get("org");
            // 提取 'id' 和 'url' 信息
            if (orgNode.has("id") && orgNode.has("url")) {
                Long orgId = orgNode.get("id").asLong();
                String orgUrl = orgNode.get("url").asText();
                addOrgToMap(orgId,orgUrl);
                addUserOrgToMap(actorId,orgId);
            }
        }
    }

    //写入数据库
    private void writeToDatabase() {
        for (User user : userMap.values()) {
            databaseHandlerService.writeUser(user);
        }

        for (Contri contri : contriMap.values()) {
            try {
                databaseHandlerService.writeContri(contri);
            } catch (SQLException e) {
                System.err.println("Error while writing Contri to the database: " + e.getMessage());
                e.printStackTrace();
            }
        }

        for (Repo repo : repoMap.values()) {
            try {
                databaseHandlerService.writeRepo(repo);
            } catch (SQLException e) {
                System.err.println("Error while writing Repo to the database: " + e.getMessage());
                e.printStackTrace();
            }
        }
        for (Org org : orgMap.values()) {
            databaseHandlerService.writeOrg(org);
        }

        for (UserOrg userOrg : userOrgMap.values()) {
            try {
                databaseHandlerService.writeUserOrg(userOrg);
            } catch (SQLException e) {
                System.err.println("Error while writing Repo to the database: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }
    // 处理不同类型的事件的方法
    private void processEvent(JsonNode eventNode) {
        try {
            String eventType = eventNode.get("type").asText();
            setOrgAndActor(eventNode);
            switch (eventType) {
                case "PushEvent":
                    handlePushEvent(eventNode);
                    break;
                case "PullRequestEvent":
                    handlePullRequestEvent(eventNode);
                    break;
                case "IssuesEvent":
                    handleIssuesEvent(eventNode);
                    break;
                case "IssueCommentEvent":
                    handleIssueCommentEvent(eventNode);
                    break;
                case "PullRequestReviewCommentEvent":
                    handlePullRequestReviewCommentEvent(eventNode);
                    break;
                case "PullRequestReviewEvent":
                    handlePullRequestReview(eventNode);
                    break;
                case "CreateEvent":
                    handleCreateEvent(eventNode);
                    break;
                case "ForkEvent":
                    handleForkEvent(eventNode);
                    break;
                case "WatchEvent":
                    handleWatchEvent(eventNode);
                    break;
                case "ReleaseEvent":
                    handleReleaseEvent(eventNode);
                    break;
                case "MemberEvent":
                    handleMemberEvent(eventNode);
                    break;
                default:
                    System.out.println("Unhandled event type: " + eventType);
            }
        }catch (Exception e) {
            // 捕获异常，记录日志或采取其他措施
            e.printStackTrace();
            System.out.println("Error processing event: " + e.getMessage());
            // 继续处理下一个事件
        }
    }

    // 处理 PushEvent 的方法
    private void handlePushEvent(JsonNode eventNode) {
        JsonNode actorNode = eventNode.get("actor");
        JsonNode repoNode = eventNode.get("repo");
        JsonNode payloadNode = eventNode.get("payload");

        Long actorId = actorNode.get("id").asLong();
        Long repoId = repoNode.get("id").asLong();
        Long distinctSize = payloadNode.get("distinct_size").asLong(); // 使用 distinct_size 字段

        // 组合键，用于唯一标识 actorId 和 repoId 的组合
        String key = actorId + "_" + repoId;

        if (contriMap.containsKey(key)) {
            // 如果字典中已经存在该组合键，累加 numPush
            Contri existingContri = contriMap.get(key);
            existingContri.setNumPush(existingContri.getNumPush() + distinctSize);
        } else {
            // 否则，新建 Contri 对象并添加到字典中
            Contri newContri = new Contri();
            newContri.setRepoId(repoId);
            newContri.setUserId(actorId);
            newContri.setNumPush(distinctSize);
            contriMap.put(key, newContri);
        }
    }

    // 处理 PullRequestEvent 的方法
    private void handlePullRequestEvent(JsonNode eventNode) {
        JsonNode actorNode = eventNode.get("actor");
        Long actorId = actorNode.get("id").asLong();
        JsonNode payloadNode = eventNode.get("payload");

        Long acceptedRepoId = payloadNode.get("pull_request").get("head").get("repo").get("id").asLong(); // 获取接受仓库 ID
        String action = payloadNode.get("action").asText(); // 获取 PR 操作类型
        boolean isMerged = payloadNode.get("pull_request").get("merged").asBoolean(); // 判断 PR 是否被合并
        Long commitCount = payloadNode.get("pull_request").get("commits").asLong(); // 获取 PR 提交数量

        String key = actorId + "_" + acceptedRepoId;

        if ("opened".equals(action) ) {
            if (repoMap.containsKey(acceptedRepoId)) {
                Repo existingRepo = repoMap.get(acceptedRepoId);
                existingRepo.setNumPR(existingRepo.getNumPR() + 1);
            } else {
                Repo newRepo = new Repo();
                newRepo.setId(acceptedRepoId);
                newRepo.setNumPR(1L);
                repoMap.put(acceptedRepoId, newRepo);
            }
            // 仅当 PR 被打开且合并时才处理
            if(isMerged) {
                if (contriMap.containsKey(key)) {
                    // 如果字典中已经存在该组合键，累加 numPush
                    Contri existingContri = contriMap.get(key);
                    existingContri.setNumPR(existingContri.getNumPR() + commitCount);
                } else {
                    // 否则，新建 Contri 对象并添加到字典中
                    Contri newContri = new Contri();
                    newContri.setRepoId(acceptedRepoId);
                    newContri.setUserId(actorId);
                    newContri.setNumPR(commitCount);
                    contriMap.put(key, newContri);
                }
            }
        }else if("closed".equals(action) || "merged".equals(action)) {
            if (repoMap.containsKey(acceptedRepoId)) {
                Repo existingRepo = repoMap.get(acceptedRepoId);
                existingRepo.setNumSolvePR(existingRepo.getNumSolvePR() + 1);
            } else {
                Repo newRepo = new Repo();
                newRepo.setId(acceptedRepoId);
                newRepo.setNumSolvePR(1L);
                repoMap.put(acceptedRepoId, newRepo);
            }
        }
    }

    // 处理 IssuesEvent 的方法
    private void handleIssuesEvent(JsonNode eventNode) {
        JsonNode payloadNode = eventNode.get("payload");
        JsonNode repoNode = eventNode.get("repo");

        Long repoId = repoNode.get("id").asLong(); // 获取 repo.id 作为仓库 ID
        String action = payloadNode.get("action").asText(); // 获取 action 字段

        if ("closed".equals(action)) {
            // 当 action 为 closed 时，更新相应的 numSolveIssue 和 numIssue
            if (repoMap.containsKey(repoId)) {
                // 如果字典中已经存在该仓库 ID，更新 numSolveIssue 和 numIssue
                Repo existingRepo = repoMap.get(repoId);
                existingRepo.setNumSolveIssue(existingRepo.getNumSolveIssue() + 1); // 增加解决的问题数量
            } else {
                // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numSolveIssue 和 numIssue
                Repo newRepo = new Repo();
                newRepo.setId(repoId);
                newRepo.setNumSolveIssue(1L); // 初始化 numSolveIssue 为 1
                repoMap.put(repoId, newRepo);
            }
        }else if("opened".equals(action)) {
            // 当 action 为 closed 时，更新相应的 numSolveIssue 和 numIssue
            if (repoMap.containsKey(repoId)) {
                // 如果字典中已经存在该仓库 ID，更新 numSolveIssue 和 numIssue
                Repo existingRepo = repoMap.get(repoId);
                existingRepo.setNumIssue(existingRepo.getNumIssue() + 1); // 增加总问题数量
            } else {
                // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numSolveIssue 和 numIssue
                Repo newRepo = new Repo();
                newRepo.setId(repoId);
                newRepo.setNumIssue(1L); // 初始化 numIssue 为 1
                repoMap.put(repoId, newRepo);
            }
        }
    }

    // 处理 IssueCommentEvent 的方法
    private void handleIssueCommentEvent(JsonNode eventNode) {
        handlePullRequestReviewCommentEvent(eventNode);   //处理逻辑一致
    }

    private void handlePullRequestReview(JsonNode eventNode) {
        handlePullRequestReviewCommentEvent(eventNode);   //处理逻辑一致
    }

    // 处理 PullRequestReviewEvent 的方法
    private void handlePullRequestReviewCommentEvent(JsonNode eventNode) {
        JsonNode actorNode = eventNode.get("actor");
        JsonNode repoNode = eventNode.get("repo");

        Long actorId = actorNode.get("id").asLong();
        Long repoId = repoNode.get("id").asLong();

        // 组合键，用于唯一标识 actorId 和 repoId 的组合
        String key = actorId + "_" + repoId;

        if (contriMap.containsKey(key)) {
            // 如果字典中已经存在该组合键，累加 numSolveIssue
            Contri existingContri = contriMap.get(key);
            existingContri.setNumSolveIssue(existingContri.getNumSolveIssue() + 1);
        } else {
            // 否则，新建 Contri 对象并初始化 numSolveIssue 为 1
            Contri newContri = new Contri();
            newContri.setRepoId(repoId);
            newContri.setUserId(actorId);
            newContri.setNumSolveIssue(1L);
            contriMap.put(key, newContri);
        }
    }

    // 处理 CreateEvent 的方法
    private void handleCreateEvent(JsonNode eventNode) {
        JsonNode actorNode = eventNode.get("actor");
        JsonNode repoNode = eventNode.get("repo");
        JsonNode payloadNode = eventNode.get("payload");

        Long actorId = actorNode.get("id").asLong();
        Long repoId = repoNode.get("id").asLong();
        String refType = payloadNode.get("ref_type").asText(); // 获取 ref_type 字段
        // 组合键，用于唯一标识 actorId 和 repoId 的组合
        String key = actorId + "_" + repoId;

        if (contriMap.containsKey(key)) {
            // 如果字典中已存在该组合键
            Contri existingContri = contriMap.get(key);
            if ("branch".equals(refType) || "tag".equals(refType)) {
                // 增加 numCreate
                existingContri.setNumCreate(existingContri.getNumCreate() + 1);
            } else if ("repository".equals(refType)) {
                // 设置 isOwner 为 true
                existingContri.setOwner(true);
            }
        } else {
            // 否则，新建 Contri 对象并根据条件初始化
            Contri newContri = new Contri();
            newContri.setUserId(actorId);
            newContri.setRepoId(repoId);
            if ("branch".equals(refType) || "tag".equals(refType)) {
                newContri.setNumCreate(1L); // 初始化 numCreate 为 1
            } else if ("repository".equals(refType)) {
                newContri.setOwner(true); // 初始化 isOwner 为 true
            }
            contriMap.put(key, newContri);
        }
    }

    // 处理 ForkEvent 的方法
    private void handleForkEvent(JsonNode eventNode) {
        JsonNode payloadNode = eventNode.get("payload");
        JsonNode forkeeNode = payloadNode.get("forkee");

        Long forkeeId = forkeeNode.get("id").asLong(); // 获取原仓库 ID

        if (repoMap.containsKey(forkeeId)) {
            // 如果字典中已经存在该仓库 ID，更新 numFork 字段
            Repo existingRepo = repoMap.get(forkeeId);
            existingRepo.setNumFork(existingRepo.getNumFork() + 1);
        } else {
            // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numFork 为 1
            Repo newRepo = new Repo();
            newRepo.setId(forkeeId);
            newRepo.setNumFork(1L); // 初始化 numFork 为 1
            repoMap.put(forkeeId, newRepo);
        }
    }

    // 处理 WatchEvent 的方法
    private void handleWatchEvent(JsonNode eventNode) {
        JsonNode repoNode = eventNode.get("repo");

        Long repoId = repoNode.get("id").asLong(); // 获取 repo.id 作为仓库 ID

        if (repoMap.containsKey(repoId)) {
            // 如果字典中已经存在该仓库 ID，更新 numWatch 字段
            Repo existingRepo = repoMap.get(repoId);
            existingRepo.setNumWatch(existingRepo.getNumWatch() + 1);
        } else {
            // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numWatch 为 1
            Repo newRepo = new Repo();
            newRepo.setId(repoId);
            newRepo.setNumWatch(1L); // 初始化 numWatch 为 1
            repoMap.put(repoId, newRepo);
        }
    }
    // 处理 ReleaseEvent 的方法
    private void handleReleaseEvent(JsonNode eventNode) {
        JsonNode repoNode = eventNode.get("repo");

        Long repoId = repoNode.get("id").asLong(); // 获取 repo.id 作为仓库 ID

        if (repoMap.containsKey(repoId)) {
            // 如果字典中已经存在该仓库 ID，更新 numWatch 字段
            Repo existingRepo = repoMap.get(repoId);
            existingRepo.setNumRelease(existingRepo.getNumRelease() + 1);
        } else {
            // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numWatch 为 1
            Repo newRepo = new Repo();
            newRepo.setId(repoId);
            newRepo.setNumRelease(1L); // 初始化 numWatch 为 1
            repoMap.put(repoId, newRepo);
        }
    }
    // 处理 MemberEvent 的方法
    private void handleMemberEvent(JsonNode eventNode) {
        JsonNode repoNode = eventNode.get("repo");
        JsonNode payloadNode = eventNode.get("payload");

        Long repoId = repoNode.get("id").asLong(); // 获取 repo.id 作为仓库 ID
        String action = payloadNode.get("action").asText(); // 获取 action 字段

        if ("added".equals(action)) {
            if (repoMap.containsKey(repoId)) {
                // 如果字典中已经存在该仓库 ID，更新 numMember 字段
                Repo existingRepo = repoMap.get(repoId);
                existingRepo.setNumMember(existingRepo.getNumMember() + 1);
            } else {
                // 如果字典中不存在该仓库 ID，创建新 Repo 对象并初始化 numMember 为 1
                Repo newRepo = new Repo();
                newRepo.setId(repoId);
                newRepo.setNumMember(1L); // 初始化 numMember 为 1
                repoMap.put(repoId, newRepo);
            }
        }
    }
}
